﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YAPL.Language.Ast.Transformation.Utils;
using YAPL.Language.Ast.Utils;
using YAPL.Language.Core.Types;

namespace YAPL.Language.Ast.Transformation {
	public class OperatorPrecomputationTransform : AstTransformBase, IAstTransform {
		public override Expression Transform(Expression source, out bool treeModified) {
			treeModified = false;

			var root = source;
			var walker = new DefaultAstWalker(root, AstWalkType.PostOrder);
			Expression newExpr = root;
			while (walker.MoveNext()) {

				newExpr = walker.Current;
				if (walker.Current is BinaryExpression) {
					newExpr = PrecomputeBinaryExpression(walker.Current as BinaryExpression);
				}
				else if (walker.Current is UnaryExpression) {
					newExpr = PrecomputeUnaryExpression(walker.Current as UnaryExpression);
				}

				if (!ReferenceEquals(newExpr, walker.Current)) {
					treeModified = true;
					walker.RewriteCurrent(newExpr);
				}
			}

			return newExpr;
		}


		private static Expression PrecomputeBinaryExpression(BinaryExpression current) {
			if (current.Left is ConstantExpression && current.Right is ConstantExpression) {
				var leftYAPL = YAPLObject.CreateFromObject(current.Left.Type, ((ConstantExpression) current.Left).Value);
				var rightYAPL = YAPLObject.CreateFromObject(current.Right.Type, ((ConstantExpression)current.Right).Value);
				var operatorMethod = current.GetOperatorMethod();
				var result = (YAPLObject)operatorMethod.Invoke(leftYAPL, new[] {rightYAPL});
				return Expressions.Constant(result.GetType(), result.Value);
			}
			return current;
		}

		private static Expression PrecomputeUnaryExpression(UnaryExpression current) {
			if (current.Operand is ConstantExpression) {
				var yaplValue = YAPLObject.CreateFromObject(current.Operand.Type, ((ConstantExpression)current.Operand).Value);
				var operatorMethod = current.GetOperatorMethod();
				var result = (YAPLObject)operatorMethod.Invoke(yaplValue, Type.EmptyTypes);
				return Expressions.Constant(result.GetType(), result.Value);
			}
			return current;
		}

	}
}
